﻿namespace NTLib.WPF.ViewModels
{
    using Core.Notifier;
    using Core.ScopeLock;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;

    /// <summary>
    /// 
    /// </summary>
    /// <seealso cref="NTLib.Core.Notifier.BaseNotifier" />
    public abstract class BaseViewModel : BaseNotifier
    {
        #region Fields

        private long _workCounter;
        private bool _isEnabled;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether this instance is enabled.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is enabled; otherwise, <c>false</c>.
        /// </value>
        public bool IsEnabled
        {
            get { return this._isEnabled; }
            set
            {
                if (SafeSetValue(ref this._isEnabled, value))
                    NotifyWorkingAndEnabledChanged();
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is working.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is working; otherwise, <c>false</c>.
        /// </value>
        public bool IsWorking
        {
            get { return Interlocked.Read(ref this._workCounter) > 0; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Starts the work.
        /// </summary>
        public IDisposable StartWork()
        {
            var counter = Interlocked.Increment(ref this._workCounter);
            NotifyWorkingAndEnabledChanged();

            return new ScopeLockItem<long>(counter, (c) =>
            {
                NotifyWorkingAndEnabledChanged();

                Interlocked.Decrement(ref this._workCounter);
            }
            );
        }

        /// <summary>
        /// Will set the value and notify the UI thread using the Dispatcher.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="prop">The property.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected bool SafeSetValue<T>(ref T prop, T value)
        {
            var result = false;
            T localProp = prop;
            DispatchHelper.DispatchExec(() =>
            {
                var lambdaProp = localProp;
                result = this.SetValue(ref lambdaProp, value);
                if (result)
                    localProp = lambdaProp;
            });

            if (result)
                prop = localProp;
            return result;
        }

        /// <summary>
        /// Safes the on property changed asynchronous.
        /// </summary>
        /// <param name="callerMemberName">Name of the caller member.</param>
        /// <returns></returns>
        protected async Task SafeOnPropertyChangedAsync([CallerMemberName]string callerMemberName = null)
        {
            await DispatchHelper.DispatchExecAsync((c) =>
            {
                this.OnPropertyChanged(callerMemberName);
            });
        }

        /// <summary>
        /// Notifies the working and enabled changed.
        /// </summary>
        protected async void NotifyWorkingAndEnabledChanged()
        {
            await SafeOnPropertyChangedAsync(nameof(IsWorking));
            await SafeOnPropertyChangedAsync(nameof(IsEnabled));
        }

        #endregion
    }
}
